#include "codec.h"
#include <muduo/base/Logging.h>
#include <muduo/base/Mutex.h>
#include <muduo/net/EventLoopThread.h>
#include <muduo/net/TcpClient.h>
#include <boost/bind.hpp>
#include <boost/noncopyable.hpp>
#include <iostream>
#include <stdio.h>

using namespace muduo;
using namespace muduo::net;

class ChatClient : boost::noncopyable {
public:
	ChatClient(EventLoop *loop, const InetAddress &serverAddr)
		: client_(loop, serverAddr, "ChatClient"),
		  codec_(boost::bind(&ChatClient::OnStringMessage, this, _1, _2, _3)) {
		client_.setConnectionCallback(boost::bind(&ChatClient::OnConnection, this, _1));
		client_.setMessageCallback(boost::bind(&LengthHeaderCodec::OnMessage, &codec_, _1, _2, _3));
		client_.enableRetry();
	}

	void Connect() {
		client_.connect();
	}

	void Disconnect() {
		client_.disconnect();
	}

	// 由 main 线程调用。
	void Write(const StringPiece &message) {
		MutexLockGuard lock(mutex_); // 用于锁住 connection_ 是否有效
		if (connection_)
			codec_.Send(boost::get_pointer(connection_), message);
	}

private:
	// 由 loop 线程调用。
	void OnConnection(const TcpConnectionPtr &conn) {
		LOG_INFO << conn->localAddress().toIpPort() << " -> "
		         << conn->peerAddress().toIpPort() << " is "
		         << (conn->connected() ? "UP" : "DOWN");
		MutexLockGuard lock(mutex_);
		if (conn->connected())
			connection_ = conn;
		else
			connection_.reset();
	}

	void OnStringMessage(const TcpConnectionPtr &, const string &message, Timestamp) {
		printf("<<< %s\n", message.c_str());
	}

	TcpClient client_;
	LengthHeaderCodec codec_;
	MutexLock mutex_;
	TcpConnectionPtr connection_;
};

int 
main(int argc, char* argv[]) {
    LOG_INFO << "pid = " << getpid();
    if (argc > 2) {
        EventLoopThread loopThread;
        uint16_t port = static_cast<uint16_t>(atoi(argv[2]));
        InetAddress serverAddr(argv[1], port);

        ChatClient client(loopThread.startLoop(), serverAddr);
        client.Connect();
        std::string line;
        while (std::getline(std::cin, line)) {
        	if (line == "exit")
        		break;
        	client.Write(line);
        }
        client.Disconnect();
        CurrentThread::sleepUsec(1000 * 1000);  // wait for disconnect, see ace/logging/client.cc
    } else {
        printf("Usage: %s host_ip port\n", argv[0]);
    }
}
